
#include "TankWar.h"
#include "MatrixConvert.h"
#include "TrigMath.h"

#include "ObjInst.h"
#include "AppEnv.h"


CTankWar::CTankWar()
{
	float init[] = {0,40,0,1};

	init[0] = 0;
	init[1] = 40;
	init[2] = 0;
	init[3] = 1;
	cam_pos		= init;

	init[0] = 0;
	init[1] = 0;
	init[2] = 0;
	init[3] = 1;
	cam_dir		= init;

	init[0] = 0;
	init[1] = 0;
	init[2] = 0;
	init[3] = 1;
	cam_target	= init;

	init[0] = 1.0;
	init[1] = 1.0;
	init[2] = 1.0;
	init[3] = 1;
	vscale	= init;

	init[0] = 0;
	init[1] = 0;
	init[2] = 0;
	init[3] = 1;
	vpos	= init;

	init[0] = 0;
	init[1] = 0;
	init[2] = 0;
	init[3] = 1;
	vrot	= init;
}
	
CTankWar::~CTankWar()
{

}

int CTankWar::Game_Init()
{
	// this function is where you do all the initialization 
	// for your game

	int index; // looping var

	// start up DirectDraw (replace the parms as you desire)
	int nRet = m_ddraw.Init(m_hWnd, m_WndWidth, m_WndHeight, m_bpp, m_bFullScreen);
	//if (nRet != 0)
	//	return-1;

	m_graphGdi.Init(&m_ddraw);

	// initialize directinput
	m_gameInput.Init(m_hInstance, m_hWnd);

	// acquire the keyboard 
	m_gameInput.Init_Keyboard();


	// initialize directsound and directmusic
	m_gameSound.Init(m_hWnd);
	m_gameMusic.Init(m_hWnd, m_gameSound.GetDSound());

	// hide the mouse
	ShowCursor(FALSE);

	// seed random number generator
	srand(m_gameClock.Start_Clock());

	//
	CTrigMath::Build_Sin_Cos_Tables();


	// initialize the camera with 90 FOV, normalized coordinates
	cam.Init(
		CAM_MODEL_EULER, // euler camera model
		cam_pos,  // initial camera position
		cam_dir,  // initial camera angles
		cam_target,      // no initial target
		200,      // near and far clipping planes
		12000.0,
		90.0,      // field of view in degrees
		m_WndWidth,   // size of final screen viewport
		m_WndHeight);

	CObjInst<CAppEnv>::Inst()->Init();
	tstring strBasePath = CObjInst<CAppEnv>::Inst()->m_strAppPath + _T("../../res/");

	// load the master tank object
	tstring strPath = strBasePath + _T("tank2.plg");
	vscale.SetX(0.75);
	vscale.SetY(0.75);
	vscale.SetZ(0.75);
	m_plgFile.Init((TCHAR*)strPath.c_str());
	m_plgFile.LoadObject(obj_tank, &vscale, &vpos, &vrot);


	// load player object for 3rd person view
	strPath = strBasePath + _T("tank3.plg");
	vscale.SetX(0.75);
	vscale.SetY(0.75);
	vscale.SetZ(0.75);
	m_plgFile.Init((TCHAR*)strPath.c_str());
	m_plgFile.LoadObject(obj_player, &vscale, &vpos, &vrot);


	// load the master tower object
	strPath = strBasePath + _T("tower1.plg");
	vscale.SetX(1.0);
	vscale.SetY(2.0);
	vscale.SetZ(1.0);
	m_plgFile.Init((TCHAR*)strPath.c_str());
	m_plgFile.LoadObject(obj_tower, &vscale, &vpos, &vrot);


	// load the master ground marker
	strPath = strBasePath + _T("marker1.plg");
	vscale.SetX(3.0);
	vscale.SetY(3.0);
	vscale.SetZ(3.0);
	m_plgFile.Init((TCHAR*)strPath.c_str());
	m_plgFile.LoadObject(obj_marker, &vscale, &vpos, &vrot);


	// position the tanks
	for (index = 0; index < NUM_TANKS; index++)
	{
		// randomly position the tanks
		tanks[index].vector[X] = RAND_RANGE(-UNIVERSE_RADIUS, UNIVERSE_RADIUS);
		tanks[index].vector[Y] = 0; // obj_tank.max_radius;
		tanks[index].vector[Z] = RAND_RANGE(-UNIVERSE_RADIUS, UNIVERSE_RADIUS);
		tanks[index].vector[W] = RAND_RANGE(0,360);
	} // end for


	// position the towers
	for (index = 0; index < NUM_TOWERS; index++)
	{
		// randomly position the tower
		towers[index].vector[X] = RAND_RANGE(-UNIVERSE_RADIUS, UNIVERSE_RADIUS);
		towers[index].vector[Y] = 0; // obj_tower.max_radius;
		towers[index].vector[Z] = RAND_RANGE(-UNIVERSE_RADIUS, UNIVERSE_RADIUS);
	} // end for

	// return success
	return 0;

} // end Game_Init


int	CTankWar::Game_Main()
{
	// this is the workhorse of your game it will be called
	// continuously in real-time this is like main() in C
	// all the calls for you game go here!

	static CMatrix44 mrot;   // general rotation matrix

	// these are used to create a circling camera
	static float view_angle = 0; 
	static float camera_distance = 6000;
	static CVector4d pos;
	static float tank_speed;
	static float turning = 0;

	int index; // looping var

	// start the timing clock
	m_gameClock.Start_Clock();

	// clear the drawing surface 
	m_ddraw.Fill_Surface(BACK_SURFACE, 0);

		// draw the sky
	m_graphGdi.Draw_Rectangle(0,0, m_WndWidth-1, m_WndHeight/2, RGB16Bit(0,140,192));

	// draw the ground
	m_graphGdi.Draw_Rectangle(0,m_WndHeight/2, m_WndWidth-1, m_WndHeight-1, RGB16Bit(103,62,3));

	// read keyboard and other devices here
	m_gameInput.Read_Keyboard();

	// game logic here...
	rend_list.Reset();

	// turbo
	if (m_gameInput.GetKeyState(DIK_SPACE))
		tank_speed = 5*TANK_SPEED;
	else
		tank_speed = TANK_SPEED;

	// forward/backward
	if (m_gameInput.GetKeyState(DIK_UP))
	{
		// move forward
		cam.m_pos.vector[X] += tank_speed * CTrigMath::Fast_Sin(cam.m_dir[Y]);
		cam.m_pos.vector[Z] += tank_speed * CTrigMath::Fast_Cos(cam.m_dir[Y]);
	} // end if

	if (m_gameInput.GetKeyState(DIK_DOWN))
	{
		// move backward
		cam.m_pos.vector[X] -= tank_speed * CTrigMath::Fast_Sin(cam.m_dir[Y]);
		cam.m_pos.vector[Z] -= tank_speed * CTrigMath::Fast_Cos(cam.m_dir[Y]);
	} // end if

	// rotate
	if (m_gameInput.GetKeyState(DIK_RIGHT))
	{
		cam.m_dir.vector[Y] += 3; 

		// add a little turn to object
		if ((turning+=2) > 15)
			turning=15;

	} // end if

	if (m_gameInput.GetKeyState(DIK_LEFT))
	{
		cam.m_dir.vector[Y] -= 3; 

		// add a little turn to object
		if ((turning-=2) < -15)
			turning=-15;

	} // end if
	else // center heading again
	{
		if (turning > 0)
			turning-=1;
		else
			if (turning < 0)
				turning+=1;

	} // end else

	// generate camera matrix
	cam.Build_Matrix_Euler(CAM_ROT_SEQ_ZYX);

	// lock the back buffer
	m_ddraw.Lock_Back_Surface();

	RECT clipRect = {0, 0, m_WndWidth-1, m_WndHeight-1};
	obj_tank.Init(m_ddraw.GetBackBuffer(), m_ddraw.GetBackLpitch(), clipRect, &cam);
	obj_player.Init(m_ddraw.GetBackBuffer(), m_ddraw.GetBackLpitch(), clipRect, &cam);
	obj_tower.Init(m_ddraw.GetBackBuffer(), m_ddraw.GetBackLpitch(), clipRect, &cam);
	obj_marker.Init(m_ddraw.GetBackBuffer(), m_ddraw.GetBackLpitch(), clipRect, &cam);
	rend_list.Init(m_ddraw.GetBackBuffer(), m_ddraw.GetBackLpitch(), clipRect, &cam);

	// insert the tanks in the world
	for (index = 0; index < NUM_TANKS; index++)
	{
		// reset the object (this only matters for backface and object removal)
		obj_tank.Reset();

		// generate rotation matrix around y axis
		XYZ_Rotation_MATRIX4X4(0, tanks[index][W], 0, mrot);

		// rotate the local coords of the object
		obj_tank.Transform(mrot, TRANSFORM_LOCAL_TO_TRANS,1);

		// set position of tank
		obj_tank.world_pos.vector[X] = tanks[index][X];
		obj_tank.world_pos.vector[Y] = tanks[index][Y];
		obj_tank.world_pos.vector[Z] = tanks[index][Z];

		// attempt to cull object   
		if (obj_tank.Cull(CULL_OBJECT_XYZ_PLANES))
		{
			// if we get here then the object is visible at this world position
			// so we can insert it into the rendering list
			// perform local/model to world transform
			obj_tank.Model_To_World(TRANSFORM_TRANS_ONLY);

			// insert the object into render list
			rend_list.Insert_Obj4d(obj_tank);
			//rend_list.Push_Back(&obj_tank);
		} // end if

	} // end for

	// insert the player into the world
	// reset the object (this only matters for backface and object removal)
	obj_player.Reset();

	// set position of tank
	obj_player.world_pos.vector[X] = cam.m_pos[X] + 300 * CTrigMath::Fast_Sin(cam.m_dir[Y]);
	obj_player.world_pos.vector[Y] = cam.m_pos[Y] - 70;
	obj_player.world_pos.vector[Z] = cam.m_pos[Z] + 300 * CTrigMath::Fast_Cos(cam.m_dir[Y]);

	// generate rotation matrix around y axis
	XYZ_Rotation_MATRIX4X4(0, cam.m_dir[Y] + turning, 0, mrot);

	// rotate the local coords of the object
	obj_player.Transform(mrot, TRANSFORM_LOCAL_TO_TRANS,1);

	// perform world transform
	obj_player.Model_To_World(TRANSFORM_TRANS_ONLY);

	// insert the object into render list
	rend_list.Insert_Obj4d(obj_player);
	//rend_list.Push_Back(&obj_player);


	//
	// insert the towers in the world
	for (index = 0; index < NUM_TOWERS; index++)
	{
		// reset the object (this only matters for backface and object removal)
		obj_tower.Reset();

		// set position of tower
		obj_tower.world_pos.vector[X] = towers[index][X];
		obj_tower.world_pos.vector[Y] = towers[index][Y];
		obj_tower.world_pos.vector[Z] = towers[index][Z];

		// attempt to cull object   
		if (obj_tower.Cull(CULL_OBJECT_XYZ_PLANES))
		{
			// if we get here then the object is visible at this world position
			// so we can insert it into the rendering list
			// perform local/model to world transform
			obj_tower.Model_To_World();

			// insert the object into render list
			rend_list.Insert_Obj4d(obj_tower);
		} // end if

	} // end for

	// seed number generator so that modulation of markers is always the same
	srand(13);
	//

	// insert the ground markers into the world
	for (int index_x = 0; index_x < NUM_POINTS_X; index_x++)
	{
		for (int index_z = 0; index_z < NUM_POINTS_Z; index_z++)
		{
			// reset the object (this only matters for backface and object removal)
			obj_marker.Reset();

			// set position of tower
			obj_marker.world_pos.vector[X] = RAND_RANGE(-100,100)-UNIVERSE_RADIUS+index_x*POINT_SIZE;
			obj_marker.world_pos.vector[Y] = obj_marker.max_radius;
			obj_marker.world_pos.vector[Z] = RAND_RANGE(-100,100)-UNIVERSE_RADIUS+index_z*POINT_SIZE;

			// attempt to cull object   
			if (obj_marker.Cull(CULL_OBJECT_XYZ_PLANES))
			{
				// if we get here then the object is visible at this world position
				// so we can insert it into the rendering list
				// perform local/model to world transform
				obj_marker.Model_To_World();

				// insert the object into render list
				rend_list.Insert_Obj4d(obj_marker);
			} // end if

		}
	}// end for

	// remove backfaces
	rend_list.Remove_Backfaces();

	// apply world to camera transform
	rend_list.World_To_Camera();

	// apply camera to perspective transformation
	rend_list.Camera_To_Perspective();

	// apply screen transform
	rend_list.Perspective_To_Screen();

	// draw instructions
		// draw instructions
	m_graphGdi.Draw_Text("Press ESC to exit. Use UP ARROW and DOWN ARROW to rotate.", 0, 0, RGB(0,255,0));

	// render the polygon list
	//rend_list.Draw_Wire16();
	rend_list.Draw_Solid16();

	// unlock the back buffer
	m_ddraw.Unlock_Back_Surface();

	// flip the surfaces
	m_ddraw.Flip(m_Window_client_x0, m_Window_client_y0);

	// sync to 30ish fps
	m_gameClock.Wait_Clock(30);

	// check of user is trying to exit
	if (KEY_DOWN(VK_ESCAPE) || m_gameInput.GetKeyState(DIK_ESCAPE))
	{
		PostMessage(m_hWnd, WM_DESTROY,0,0);

	} // end if

	return 0;
}

int	CTankWar::Game_Shutdown()
{
	// this function is where you shutdown your game and
	// release all resources that you allocated

	// shut everything down

	// release all your resources created for the game here....


	CGameMain::Game_Shutdown();

	return 0;
}
